package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.constant.Constants;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IDeviceService;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.DeviceReportDataVo;
import com.zzyl.nursing.vo.ProductVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.*;

/**
 * 设备Service业务层处理
 * 
 * @author ghy
 * @date 2025-08-21
 */
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService
{
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private IoTDAClient client;
    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 查询设备
     * 
     * @param id 设备主键
     * @return 设备
     */
    @Override
    public Device selectDeviceById(Long id)
    {
        return deviceMapper.selectById(id);
    }

    /**
     * 查询设备列表
     * 
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<Device> selectDeviceList(Device device)
    {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增设备
     * 
     * @param device 设备
     * @return 结果
     */
    @Override
    public int insertDevice(Device device)
    {
        return deviceMapper.insert(device);
    }

    /**
     * 修改设备
     * 
     * @param device 设备
     * @return 结果
     */
    @Override
    public int updateDevice(Device device)
    {
        return deviceMapper.updateById(device);
    }

    /**
     * 批量删除设备
     * 
     * @param ids 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Long[] ids)
    {
        return deviceMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除设备信息
     * 
     * @param id 设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceById(Long id)
    {
        return deviceMapper.deleteById(id);
    }


    /**
     * 同步产品列表
     */
    @Override
    public void syncProductList() {
        // 1.调用IOT平台的API获取产品列表
        ListProductsRequest request = new ListProductsRequest();
        //设置条数
        request.setLimit(50);
        //发起请求
        ListProductsResponse response = client.listProducts(request);
        if(response.getHttpStatusCode() != 200) {
            throw new BaseException("物联网接口 - 查询产品，同步失败");
        }
        // 2.写入Redis
        redisTemplate.opsForValue().set(Constants.CACHE_IOT_ALL_PRODUCTS, JSONUtil.toJsonStr(response.getProducts()));
    }

    /**
     * 查询所有产品列表
     *
     * @return
     */
    @Override
    public List<ProductVo> allProduct() {
        // 1.从Redis中获取产品列表数据
        String json = (String) redisTemplate.opsForValue().get(Constants.CACHE_IOT_ALL_PRODUCTS);
        // 2.数据封装,转换成List<ProductVo>
        if(StringUtils.isBlank(json)) {
            return new ArrayList<>();
        }
        return JSONUtil.toList(json, ProductVo.class);
    }

    /**
     * 注册设备
     *
     * @param deviceDto 设备注册参数
     */
    @Override
    public int register(DeviceDto deviceDto) {
        // 1.判断设备名称是否重复
        isExistDeviceName(deviceDto.getDeviceName());
        // 2.判断设备标记号是否重复
        isExistNodeId(deviceDto.getNodeId());
        // 3.判断同一个位置是否绑定过相同产品
        if(Objects.isNull(deviceDto.getPhysicalLocationType())) {
            deviceDto.setPhysicalLocationType(-1);
        }
        isExistProduct(deviceDto.getProductKey(), deviceDto.getBindingLocation(), deviceDto.getLocationType(), deviceDto.getPhysicalLocationType());
        // 4.保存设备到IOT
        AddDeviceResponse response = saveDeviceToIot(deviceDto);
        // 5.保存设备到数据库
        saveDevice(deviceDto, response.getDeviceId(), response.getAuthInfo().getSecret());

        return 1;
    }

    /**
     * 查询设备详情
     *
     * @param iotId
     * @return
     */
    @Override
    public DeviceDetailVo getByIotId(String iotId) {
        // 1.从IOT平台获取设备详情
        ShowDeviceRequest request = new ShowDeviceRequest();
        request.withDeviceId(iotId);
        ShowDeviceResponse response = client.showDevice(request);
        if (response.getHttpStatusCode() != 200) {
            throw new BaseException("从IOT平台获取设备详情数据失败");
        }
        String status = response.getStatus();
        String activeTimeStr = response.getActiveTime();
        // 2.根据设备ID查询数据库获取设备数据
        Device device = this.lambdaQuery()
                .eq(Device::getIotId, iotId)
                .one();
        if(Objects.isNull(device)) {
            throw new BaseException("设备不存在");
        }
        DeviceDetailVo detailVo = BeanUtil.toBean(device, DeviceDetailVo.class);
        detailVo.setDeviceStatus(status);
        if(StringUtils.isNotBlank(activeTimeStr)) {
            // 将字符串转换成LocalDateTime
            LocalDateTime activeTime = LocalDateTimeUtil.parse(activeTimeStr, DatePattern.UTC_MS_PATTERN);
            // 时间差8个小时，进行时区转换
            activeTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                    .withZoneSameInstant(ZoneId.systemDefault())
                    .toLocalDateTime();
            detailVo.setActiveTime(activeTime);
        }
        // 3.数据封装
        return detailVo;
    }

    /**
     * 查询设备上报数据
     *
     * @param iotId
     * @return
     */
    @Override
    public List<DeviceReportDataVo> queryServiceProperties(String iotId) {
        List<DeviceReportDataVo> voList = new ArrayList<>();
        try {
            ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
            request.withDeviceId(iotId);
            // 根据设备从IOT平台获取上报数据
            ShowDeviceShadowResponse response = client.showDeviceShadow(request);
            if (response.getHttpStatusCode() != 200) {
                throw new BaseException("从IOT平台获取设备详情数据失败");
            }
            List<DeviceShadowData> list = response.getShadow();
            if(CollUtil.isEmpty(list)) {
                // 集合中没有数据，表示该设备没有上报过数据，则直接返回空集合
                return voList;
            }
            // 遍历封装数据
            list.forEach(item -> {
                // 获取最后一次上报数据时间
                String eventTimeStr = item.getReported().getEventTime();
                // 获取上报数据
                Map<String,Object> map = (Map<String, Object>) item.getReported().getProperties();
                map.forEach((k, v) -> {
                    LocalDateTime eventTime = null;
                    if(StringUtils.isNotBlank(eventTimeStr)) {
                        // 时间差8个小时，进行时区转换
                        eventTime = LocalDateTimeUtil.parse(eventTimeStr, Constants.LOCAL_DATE_FORMATTER)
                                .atZone(ZoneId.from(ZoneOffset.UTC))
                                .withZoneSameInstant(ZoneId.systemDefault())
                                .toLocalDateTime();
                    }
                    DeviceReportDataVo vo = DeviceReportDataVo.builder()
                            .functionId(k)
                            .eventTime(eventTime)
                            .value(v)
                            .build();
                    voList.add(vo);
                } );
            });
        } catch (Exception e) {
            // e.printStackTrace();
            throw new BaseException("查询设备上报数据失败", e.getMessage());
        }
        return voList;
    }


    /**
     * 保存设备到数据库
     * @param deviceDto
     * @param deviceId
     * @param secret
     */
    private void saveDevice(DeviceDto deviceDto, String deviceId, String secret) {
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        device.setIotId(deviceId);
        device.setSecret(secret);
        if(deviceDto.getPhysicalLocationType().equals(-1)) {
            device.setHaveEntranceGuard(0);
        } else {
            device.setHaveEntranceGuard(1);
        }
        try {
            save(device);
        } catch (Exception e) {
            //TODO 删除IOT平台关联的设备
            throw new BaseException("保存设备到数据库失败了");
        }
    }


    /**
     * 保存设备到IOT
     * @param deviceDto
     */
    private AddDeviceResponse saveDeviceToIot(DeviceDto deviceDto) {
        AddDeviceRequest request = new AddDeviceRequest();
        AddDevice addDevice = new AddDevice();
        AuthInfo authInfo = new AuthInfo();
        authInfo.withAuthType("SECRET")
                .withSecret(RandomUtil.randomString(32))
                .withSecureAccess(true);
        //IOT平台会在增加设备后，自动生成设备ID
        addDevice
                .withNodeId(deviceDto.getNodeId())
                .withDeviceName(deviceDto.getDeviceName())
                .withProductId(deviceDto.getProductKey())
                .withDescription(deviceDto.getDeviceDescription())
                .withAuthInfo(authInfo);
        request.withBody(addDevice);
        AddDeviceResponse response = client.addDevice(request);
        if(response.getHttpStatusCode() != 201) {
            throw new BaseException("设备注册失败");
        }
        return response;
    }

    /**
     * 判断同一个位置是否绑定过相同产品
     * @param productKey 产品ID
     * @param bindingLocation 绑定位置
     * @param locationType 位置类型
     * @param physicalLocationType 物理位置类型
     */
    private void isExistProduct(String productKey, Long bindingLocation, Integer locationType, Integer physicalLocationType) {
        Long count = this.lambdaQuery()
                .eq(Device::getProductKey, productKey)
                .eq(Device::getBindingLocation, bindingLocation)
                .eq(Device::getLocationType, locationType)
                .eq(Device::getPhysicalLocationType, physicalLocationType)
                .count();
        if(count > 0) {
            throw new BaseException("该老人/位置已经绑定过该产品，不能重复绑定");
        }
    }

    /**
     * 判断设备标记号是否重复
     * @param nodeId
     */
    private void isExistNodeId(String nodeId) {
        Long count = this.lambdaQuery()
                .eq(Device::getNodeId, nodeId)
                .count();
        if(count > 0) {
            throw new BaseException("设备标记号【" + nodeId + "】已存在");
        }
    }

    /**
     * 判断设备名称是否重复
     * @param deviceName
     */
    private void isExistDeviceName(String deviceName) {
        Long count = this.lambdaQuery()
                .eq(Device::getDeviceName, deviceName)
                .count();
        if(count > 0) {
            throw new BaseException("设备名称【" + deviceName + "】已存在");
        }
    }



    /**
     * 根据ProductKey查询详情
     *      （服务列表及物模型列表）
     * @param productKey
     * @return
     */
    @Override
    public List<ServiceCapability> queryProduct(String productKey) {
        ShowProductResponse response = client.showProduct(new ShowProductRequest().withProductId(productKey));
        if(response.getHttpStatusCode() != 200) {
            throw new BaseException("查询产品详情失败");
        }
        return response.getServiceCapabilities();
    }
}
